class GPUProgram {
  html.WebGLProgram _handle;
  int VPosition;
  int VTexture1;
  int VTexture2;
  int VColor;
  html.WebGLUniformLocation World;
  html.WebGLUniformLocation Projection;
  html.WebGLUniformLocation Sampler1;
  html.WebGLUniformLocation Sampler2;

  html.WebGLProgram get handle() => _handle;
  
  static Graphics _graphics;
  GPUProgram(String vertexCode, String fragmentCode, [List defines]) {
    if (defines != null && defines.length > 0) {
      var codePrefixBuilder = new StringBuffer();
      for (final item in defines) {
        codePrefixBuilder.add('#define $item\n');
      }
      
      String codePrefix = codePrefixBuilder.toString();
      vertexCode   = '$codePrefix$vertexCode';
      fragmentCode = '$codePrefix$fragmentCode';
    }
    
    final gl = _graphics.gl;
    final vertexShader = gl.createShader(html.WebGLRenderingContext.VERTEX_SHADER);
    gl.shaderSource(vertexShader, vertexCode);
    gl.compileShader(vertexShader);
    if (!gl.getShaderParameter(vertexShader, html.WebGLRenderingContext.COMPILE_STATUS)) {
      String info = gl.getShaderInfoLog(vertexShader);
      throw new IllegalArgumentException('vertex shader compile failed. ($info)');
    }
    
    final fragmentShader = gl.createShader(html.WebGLRenderingContext.FRAGMENT_SHADER);
    gl.shaderSource(fragmentShader, fragmentCode);
    gl.compileShader(fragmentShader);
    if (!gl.getShaderParameter(fragmentShader, html.WebGLRenderingContext.COMPILE_STATUS)) {
      String info = gl.getShaderInfoLog(fragmentShader);
      throw new IllegalArgumentException('fragment shader compile failed. ($info)');
    }
    
    final program = gl.createProgram();
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);
    if (!gl.getProgramParameter(program, html.WebGLRenderingContext.LINK_STATUS)) {
      String info = gl.getProgramInfoLog(program);
      throw new IllegalArgumentException('program link failed. ($info)');
    }
    
    VPosition = gl.getAttribLocation(program, 'VPosition');
    VTexture1 = gl.getAttribLocation(program, 'VTexture1');
    VTexture2 = gl.getAttribLocation(program, 'VTexture2');
    VColor = gl.getAttribLocation(program, 'VColor');
    World = gl.getUniformLocation(program, 'World');
    Projection = gl.getUniformLocation(program, 'Projection');
    Sampler1 = gl.getUniformLocation(program, 'Sampler1');
    Sampler2 = gl.getUniformLocation(program, 'Sampler2');
    _handle = program;
  }
}
